﻿using System.Collections;

namespace KinonekoSoftware.Extensions.Collections.BST
{
    partial class BinaryTreeCollection<TKey, TValue>
    {

        public IEnumerator<TValue> GetEnumerator() => GetValueByMidOrder.GetEnumerator();

        IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();


        public IEnumerable<TValue> GetValueByPreOrder
        {
            get
            {
                if (Root is null)
                {
                    yield break;
                }

                var version = _version;

                foreach (var node in PreOrder())
                {
                    if (_version != version)
                    {
                        throw new InvalidOperationException("集合已经变化，无法继续迭代");
                    }

                    yield return node.Value;
                }
            }
        }


        public IEnumerable<Node> GetNodeByPreOrder
        {
            get
            {
                if (Root is null)
                {
                    yield break;
                }

                var version = _version;

                foreach (var node in PreOrder())
                {
                    if (_version != version)
                    {
                        throw new InvalidOperationException("集合已经变化，无法继续迭代");
                    }

                    yield return node;
                }
            }
        }

        public IEnumerable<TValue> GetValueByMidOrder
        {
            get
            {
                if (Root is null)
                {
                    yield break;
                }

                var version = _version;

                foreach (var node in MidOrder())
                {
                    if (_version != version)
                    {
                        throw new InvalidOperationException("集合已经变化，无法继续迭代");
                    }

                    yield return node.Value;
                }
            }
        }


        public IEnumerable<Node> GetNodeByMidOrder
        {
            get
            {
                if (Root is null)
                {
                    yield break;
                }

                var version = _version;

                foreach (var node in MidOrder())
                {
                    if (_version != version)
                    {
                        throw new InvalidOperationException("集合已经变化，无法继续迭代");
                    }

                    yield return node;
                }
            }
        }

        public IEnumerable<TValue> GetValueByPostOrder
        {
            get
            {
                if (Root is null)
                {
                    yield break;
                }

                var version = _version;

                foreach (var node in PostOrder())
                {
                    if (_version != version)
                    {
                        throw new InvalidOperationException("集合已经变化，无法继续迭代");
                    }

                    yield return node.Value;
                }
            }
        }


        public IEnumerable<Node> GetNodeByPostOrder
        {
            get
            {
                if (Root is null)
                {
                    yield break;
                }

                var version = _version;

                foreach (var node in PostOrder())
                {
                    if (_version != version)
                    {
                        throw new InvalidOperationException("集合已经变化，无法继续迭代");
                    }

                    yield return node;
                }
            }
        }

        public TValue this[TKey key]
        {
            get
            {
                var node = GetNode(key);

                if (node is null)
                {
                    return default(TValue);
                }

                return node.Value;
            }
        }


        public bool Contains(TValue item) => GetNode(item) is not null;

        /// <summary>
        /// 
        /// </summary>
        /// <param name="array"></param>
        /// <param name="arrayIndex"></param>
        public void CopyTo(TValue[] array, int arrayIndex) => MidOrder().Select(x => x.Value)
                                                                        .ToList()
                                                                        .CopyTo(array, arrayIndex);


        #region PreOrder

        public List<Node> PreOrder() => PreOrder(Root);

        public static List<Node> PreOrder(Node node)
        {
            var list = new List<Node>(32);
            PreOrderFlatten(list, node);
            return list;
        }

        public static void PreOrderFlatten(List<Node> collection, Node node)
        {
            while (true)
            {
                collection.Add(node);

                if (node.Left is not null)
                {
                    PreOrderFlatten(collection, node.Left);
                }

                if (node.Right is not null)
                {
                    node = node.Right;
                    continue;
                }

                break;
            }
        }

        #endregion

        #region MidOrder

        public List<Node> MidOrder() => MidOrder(Root);

        public static List<Node> MidOrder(Node node)
        {
            var list = new List<Node>(32);
            MidOrderFlatten(list, node);
            return list;
        }

        public static void MidOrderFlatten(List<Node> collection, Node node)
        {
            while (true)
            {
                if (node.Left is not null)
                {
                    MidOrderFlatten(collection, node.Left);
                }

                collection.Add(node);

                if (node.Right is not null)
                {
                    node = node.Right;
                    continue;
                }

                break;
            }
        }

        #endregion

        #region PostOrder

        public List<Node> PostOrder() => PostOrder(Root);

        public static List<Node> PostOrder(Node node)
        {
            var list = new List<Node>(32);
            PostOrderFlatten(list, node);
            return list;
        }

        public static void PostOrderFlatten(List<Node> collection, Node node)
        {
            if (node.Left is not null)
            {
                PostOrderFlatten(collection, node.Left);
            }

            if (node.Right is not null)
            {
                PostOrderFlatten(collection, node.Right);
            }

            collection.Add(node);
        }

        #endregion

        public bool IsReadOnly => false;
    }
}